/***************************************************************************
 *   Copyright (C) 2009 by umbacos   *
 *   $/var/mail/root   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "udm-common.h"

char* dcgi;
char* droo;
char* dbin;
char* wget;
char* ctor;
char* dwww;
char* dufb;
char* ddnl;
char* dtrk;
char* fcnf;
char* pCtor;
char* pWget;
char* argv0;
char* filter;
char* pidfile;
void * xmalloc ( size_t size );
char * escape_hyphens ( char * str, char * ch );

typedef enum {
	BE_STR,
	BE_INT,
	BE_LIST,
	BE_DICT,
} be_type;

typedef struct be_dict {
	char *key;
	struct be_node *val;
} be_dict;

typedef struct be_node {
	be_type type;
	union {
		char *s;
		long long i;
		struct be_node **l;
		struct be_dict *d;
	} val;
} be_node;

int isAscii(char *ret) {
	int i;
	int o = TRUE;
	for (i = 0; ret[i]; i++)
		o = o && (ret[i]>=0) && (ret[i]<=127);
#ifdef DEBUG
	if (!o) mylog ( "NOT ASCII: blanked value", "" );
#endif
	return o;
}

static be_node *be_alloc(be_type type)
{
	be_node *ret = malloc(sizeof(*ret));
	if (ret) {
		memset(ret, 0x00, sizeof(*ret));
		ret->type = type;
	}
	return ret;
}

static long long _be_decode_int(const char **data)
{
	char *endp;
	long long ret = strtoll(*data, &endp, 10);
	*data = endp;
	return ret;
}

static char *_be_decode_str(const char **data)
{
	/* XXX: should check for overflow/negative ... */
	long slen = _be_decode_int(data);
	char *ret = NULL;
	if (**data == ':') {
	 	ret = malloc(slen + 1);
		memcpy(ret, *data + 1, slen);
		ret[slen] = '\0';
		*data += slen + 1;
	}
#ifdef DEBUG
	char msg[2000];
	sprintf(msg,"read length %ld\tvalue:\"%s\"",slen,ret);
	mylog ( "read str:", msg );
#endif
	if (!isAscii(ret)) ret[0]='\0';
	return ret;
}

static be_node *_be_decode(const char **data)
{
	be_node *ret = NULL;

	switch (**data) {
		/* lists */
		case 'l': {
			unsigned int i = 0;

			ret = be_alloc(BE_LIST);

			++(*data);
			while (**data != 'e') {
				ret->val.l = realloc(ret->val.l, (i + 2) * sizeof(*ret->val.l));
				ret->val.l[i] = _be_decode(data);
				++i;
			}
			++(*data);

			if (i!=0) ret->val.l[i] = NULL;
			return ret;
		}

		/* dictionaries */
		case 'd': {
			unsigned int i = 0;

			ret = be_alloc(BE_DICT);

			++(*data);
			while (**data != 'e') {
				ret->val.d = realloc(ret->val.d, (i + 2) * sizeof(*ret->val.d));
				ret->val.d[i].key = _be_decode_str(data);
				ret->val.d[i].val = _be_decode(data);
				++i;
			}
			++(*data);

			if (i!=0) ret->val.d[i].val = NULL;

			return ret;
		}

		/* integers */
		case 'i': {
			ret = be_alloc(BE_INT);

			++(*data);
			ret->val.i = _be_decode_int(data);
			if (**data != 'e')
				return NULL;
			++(*data);

			return ret;
		}

		/* byte strings */
		case '0'...'9': {
			ret = be_alloc(BE_STR);

			ret->val.s = _be_decode_str(data);

			return ret;
		}

		/* invalid */
		default:
			break;
	}

	return ret;
}

be_node *be_decode(const char *data)
{
	return _be_decode(&data);
}

void be_free(be_node *node)
{
	switch (node->type) {
		case BE_STR:
			free(node->val.s);
			break;

		case BE_INT:
			break;

		case BE_LIST: {
			unsigned int i=0;
			if (node->val.l)
				for (i = 0; node->val.l[i]; ++i)
					be_free(node->val.l[i]);
				free(node->val.l);
			break;
		}

		case BE_DICT: {
			unsigned int i;
			if (node->val.d)
				for (i = 0; node->val.d[i].val; ++i) {
					free(node->val.d[i].key);
					be_free(node->val.d[i].val);
				}
			free(node->val.d);
			break;
		}
	}
	free(node);
}

static void be_dump(be_node *node)
{
	size_t i;
	char resp[500];

	switch (node->type) {
		case BE_STR:
			sprintf(resp, "'%s'", escape_hyphens ( node->val.s,"'" ) );
#ifdef DEBUG
	mylog ( "dumped  string:", resp );
#endif
			add_to_response ( resp );
			break;

		case BE_INT:
			sprintf(resp, "%lli", node->val.i);
			add_to_response ( resp );
			break;

		case BE_LIST:
			add_to_response("[");
			if (node->val.l)
				for (i = 0; node->val.l[i]; ++i) {
					if ( i > 0 ) add_to_response(",");
					be_dump(node->val.l[i]);
					}
			add_to_response( "]" );
			break;

		case BE_DICT:
			add_to_response("{");
			if (node->val.d)
				for (i = 0; node->val.d[i].val; ++i) {
					if ( strcmp ( node->val.d[i].key, "pieces" ) != 0 ) {
						if ( i > 0 ) add_to_response(",");
						sprintf(resp, "'%s':", node->val.d[i].key);
						add_to_response ( resp );
						be_dump(node->val.d[i].val);
					}
				}

			add_to_response("}");
			break;
	}
}

void do_meta( char * fil ) {

	struct stat statbuf;
	char * bencoded;
	char   ndnl[1000];
	FILE*  df;
	long long n_bytes;
	be_node * beno;

	memset ( &ndnl, 0, 1000 );

	if ( strlen ( fil ) > 0 )
		strcat ( strcat ( strcpy ( ndnl, ddnl ), "/" ), fil );
	else
		send_message ( "Unspecified file", NULL );
#ifdef DEBUG
	mylog ( "do_meta ndnl:", ndnl );
#endif
	if ( strstr ( ndnl, "/.." ) != NULL )
		send_message ( "Filename error (contains /..)", ndnl );

	if ( stat ( ndnl, &statbuf ) < 0 )
		send_message ( "File not found:", fil );

	bencoded = xmalloc ( statbuf.st_size );
	if (bencoded == NULL)
		send_message ( "Memory allocation failure", "" );

	if ( ( df = fopen ( ndnl, "rb" ) ) == NULL )
		send_message ( "Unable to access torrent file:", fil );

#ifdef DEBUG
	mylog ( "about to read torrent data", "" );
#endif
	n_bytes = fread ( bencoded, 1, statbuf.st_size, df );
#ifdef DEBUG
	mylog ( "about to bencode", "" );
#endif
	beno = be_decode(bencoded);
#ifdef DEBUG
	mylog ( "about to send response", "" );
#endif
	init_response();
	add_headers ( TRUE );
	add_to_response("{file:\"");
	add_to_response(fil);
	add_to_response("\",benc:");
	be_dump(beno);
	add_to_response("}");
	send_response();

}

int main ( int argc, char** argv ) {

	char fil[501];
	memset ( &fil, 0, 501 );

	get_directories ( strdup ( argv[0] ), FALSE );
	get_post_cookie ();

	if ( getenv ( "FORM_file" ) )
		strncpy ( fil, getenv ( "FORM_file" ), 500 );

	do_meta ( fil );

	return 0;
}
